package org.example.project.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.example.project.common.convention.exception.ClientException;
import org.example.project.dao.entity.ShortLinkDO;
import org.example.project.dao.mapper.ShortLinkMapper;
import org.example.project.dto.req.DeleteRecycleBinReqDTO;
import org.example.project.dto.req.MoveToRecycleBinReqDTO;
import org.example.project.dto.req.RecoverRecycleBinReqDTO;
import org.example.project.dto.req.ShortLinkRecycleBinPageReqDTO;
import org.example.project.dto.resp.ShortLinkPageRespDTO;
import org.example.project.service.RecycleBinService;
import org.redisson.api.RBloomFilter;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import static org.example.project.common.constant.RedisKeyConstant.EMPTY_SHORT_LINK_KEY;
import static org.example.project.common.constant.RedisKeyConstant.SHORT_LINK_GOTO_KEY;
import static org.example.project.common.enums.errorEnum.ShortLinkErrorCodeEnum.SHORT_LINK_NULL;

/**
 * @author weibin
 * @since 2025/10/13 21:33
 */
@Service
@RequiredArgsConstructor
public class RecycleBinServiceImpl extends ServiceImpl<ShortLinkMapper, ShortLinkDO> implements RecycleBinService {

    private final StringRedisTemplate stringRedisTemplate;
    private final RBloomFilter<String> shortLinkCreateCachePenetrationBloomFilter;

    @Override
    public void moveTo(MoveToRecycleBinReqDTO requestParam) {
        LambdaQueryWrapper<ShortLinkDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
                .eq(ShortLinkDO::getGid, requestParam.getGid())
                .eq(ShortLinkDO::getFullShortUrl, requestParam.getFullShortUrl())
                .eq(ShortLinkDO::getEnableStatus, 0)
                .eq(ShortLinkDO::getDelFlag, 0);
        ShortLinkDO shortLinkDO = baseMapper.selectOne(queryWrapper);
        if (shortLinkDO == null) {
            throw new ClientException(SHORT_LINK_NULL);
        }

        // 所谓移至回收站，其实就是将短链接启用状态改为禁用
        ShortLinkDO updateShortLinkDO = ShortLinkDO.builder()
                .enableStatus(1)
                .build();
        LambdaUpdateWrapper<ShortLinkDO> updateWrapper = Wrappers.lambdaUpdate(ShortLinkDO.class)
                .eq(ShortLinkDO::getGid, requestParam.getGid())
                .eq(ShortLinkDO::getFullShortUrl, requestParam.getFullShortUrl())
                .eq(ShortLinkDO::getEnableStatus, 0)
                .eq(ShortLinkDO::getDelFlag, 0);
        baseMapper.update(updateShortLinkDO, updateWrapper);

        // 去redis缓存里将对应的记录也删了！我们在创建短链接时进行了缓存预热
        stringRedisTemplate.delete(String.format(SHORT_LINK_GOTO_KEY, requestParam.getFullShortUrl()));
    }

    @Override
    public IPage<ShortLinkPageRespDTO> pageShortLink(ShortLinkRecycleBinPageReqDTO requestParam) {
        if (CollUtil.isEmpty(requestParam.getGidList())) {
            return new Page<>();
        }
        LambdaQueryWrapper<ShortLinkDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
                .in(ShortLinkDO::getGid, requestParam.getGidList())
                .eq(ShortLinkDO::getEnableStatus, 1)
                .eq(ShortLinkDO::getDelFlag, 0)
                .orderByDesc(ShortLinkDO::getUpdateTime);
        Page<ShortLinkDO> page = Page.of(requestParam.getCurrent(), requestParam.getSize());
        this.page(page, queryWrapper);
        // 不推荐这种写法，建议新造一个page对象，主要是这里的场景ShortLinkRecycleBinPageReqDTO来自于控制器方法参数，假如外部里设置searchCount为false，那这里分页查询的结果total不对了
//        requestParam.setSearchCount(true);
//        ShortLinkRecycleBinPageReqDTO page = this.page(requestParam, queryWrapper);
        return page.convert(x -> {
            ShortLinkPageRespDTO shortLinkPageRespDTO = BeanUtil.copyProperties(x, ShortLinkPageRespDTO.class);
            shortLinkPageRespDTO.setDomain("http://" + shortLinkPageRespDTO.getDomain());
            return shortLinkPageRespDTO;
        });
    }

    @Override
    public void recover(RecoverRecycleBinReqDTO requestParam) {
        LambdaQueryWrapper<ShortLinkDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
                .eq(ShortLinkDO::getGid, requestParam.getGid())
                .eq(ShortLinkDO::getFullShortUrl, requestParam.getFullShortUrl())
                .eq(ShortLinkDO::getEnableStatus, 1)
                .eq(ShortLinkDO::getDelFlag, 0);
        ShortLinkDO shortLinkDO = baseMapper.selectOne(queryWrapper);
        if (shortLinkDO == null) {
            throw new ClientException(SHORT_LINK_NULL);
        }

        // 所谓恢复，就是把状态改为启用
        ShortLinkDO updateShortLinkDO = ShortLinkDO.builder()
                .enableStatus(0)
                .build();
        LambdaUpdateWrapper<ShortLinkDO> updateWrapper = Wrappers.lambdaUpdate(ShortLinkDO.class)
                .eq(ShortLinkDO::getGid, requestParam.getGid())
                .eq(ShortLinkDO::getFullShortUrl, requestParam.getFullShortUrl())
                .eq(ShortLinkDO::getEnableStatus, 1)
                .eq(ShortLinkDO::getDelFlag, 0);
        baseMapper.update(updateShortLinkDO, updateWrapper);

        /*
        去redis里将该短链接对应的空对象删除，这个必须删，否则短链接访问不了，因为在跳转短链接的逻辑里面我们最后一步才是访问数据库，
        在这之前去检查了是否存在该短链接的空对象以避免缓存穿透
        至于创建短链接时不需要做这个，因为创建短链接时进行了预热，然后跳转逻辑里先查的短链接，再查的短链接空对象
        不需要我们删除，到期自己删除就行
         */
        stringRedisTemplate.delete(String.format(EMPTY_SHORT_LINK_KEY, requestParam.getFullShortUrl()));
    }

    @Override
    public void delete(DeleteRecycleBinReqDTO requestParam) {
        LambdaUpdateWrapper<ShortLinkDO> updateWrapper = Wrappers.lambdaUpdate(ShortLinkDO.class)
                .eq(ShortLinkDO::getGid, requestParam.getGid())
                .eq(ShortLinkDO::getFullShortUrl, requestParam.getFullShortUrl())
                .eq(ShortLinkDO::getEnableStatus, 1)
                .eq(ShortLinkDO::getDelFlag, 0);
        ShortLinkDO shortLinkDO = new ShortLinkDO();
        shortLinkDO.setDelFlag(1);
        baseMapper.update(shortLinkDO, updateWrapper);

        // fixme:布隆过滤器里也得删吧?

        // 将缓存中的数据也删除了
        stringRedisTemplate.delete(String.format(SHORT_LINK_GOTO_KEY, requestParam.getFullShortUrl()));
    }
}
